package IC.Semantics.types;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import IC.AST.Formal;
import IC.AST.Method;
import IC.AST.StaticMethod;
import IC.AST.LibraryMethod;

/**
 * represent a method semantic type 
 */
public class MethodType extends SemanticType
{
	private SemanticType returnType;
	private ArrayList<SemanticType> parametersTypes;
	private boolean isStatic;
	
	public MethodType(Method m, String className)
	{
		// use the name of the class so methods with same signatures but in different classes will be deemed different
		super(m.getName());
		this.returnType = TypeTable.getType(m.getType());
		this.parametersTypes = getFormalsTypes(m.getFormals());
		this.isStatic = ((m instanceof StaticMethod) || (m instanceof LibraryMethod));
	}
	
	public MethodType(String name, SemanticType retType, ArrayList<SemanticType> params, boolean isStatic)
	{
		super(name);
		this.returnType = retType;
		this.parametersTypes = params;
		this.isStatic = isStatic;
	}
	
	public boolean isStatic(){
		return isStatic;
	}

	@Override
	public boolean subtypeOf(SemanticType t) {
		return (t == this);
	}
	
	public SemanticType getReturnType(){
		return returnType;
	}
	
	public ArrayList<SemanticType> getParametersTypes(){
		return parametersTypes;
	}
	
	
	// we are overriding the equals for this class so we can compare between two method types in the type checking
	@Override
	public boolean equals(Object obj)
	{
		// this is so we can override the object equals method and not overload it
		if (obj == null || !(obj instanceof MethodType))
			return false;
		
		MethodType other = (MethodType) obj;

		if (!this.name.equals(other.name) || this.returnType != other.returnType || this.parametersTypes.size() != other.parametersTypes.size())
			return false; 
		else
		{   
			Iterator<SemanticType> thisIter = this.parametersTypes.iterator(); 
			Iterator<SemanticType> otherIter = other.parametersTypes.iterator();
			
			while (thisIter.hasNext()){   
				if (thisIter.next() != otherIter.next())
					return false;   
			}
		              
			return true;  
		}
	} 
	
	private static ArrayList<SemanticType> getFormalsTypes(List<Formal> formals)
	{
		if (formals == null) return null;
		ArrayList<SemanticType> types = new ArrayList<SemanticType>(formals.size());
		for (Formal f : formals)
			types.add(TypeTable.getType(f.getType()));
		return types;
	}

	public String toString()
	{
		StringBuilder str = new StringBuilder("{");
		if (!parametersTypes.isEmpty())
			str.append(parametersTypes.get(0).toString());
		for (int i = 1; i < parametersTypes.size(); i++)
			str.append(", " + parametersTypes.get(i));
		str.append(" -> " + returnType.toString() + "}");
		
		return str.toString();
	}

}
